package com.yanqu.road.server.gameplayer.module.activity.gashapon;

import com.yanqu.road.entity.activity.ActivityInfo;
import com.yanqu.road.entity.activity.gashapon.config.GashaponReward;
import com.yanqu.road.entity.activity.gashapon.data.GashaponUserData;
import com.yanqu.road.entity.enums.eGamePlayerEventType;
import com.yanqu.road.entity.enums.eLogMoneyType;
import com.yanqu.road.entity.enums.eSystemId;
import com.yanqu.road.entity.log.gashapon.LogGashaponDraw;
import com.yanqu.road.logic.bussiness.activity.GashaponBusiness;
import com.yanqu.road.pb.activity.GashaponProto;
import com.yanqu.road.server.GameServer;
import com.yanqu.road.server.gameplayer.GamePlayer;
import com.yanqu.road.server.gameplayer.module.base.GeneralModule;
import com.yanqu.road.server.gameplayer.module.player.CurrencyModule;
import com.yanqu.road.server.manger.ServerLanguageMgr;
import com.yanqu.road.server.manger.activity.ActivityMgr;
import com.yanqu.road.server.manger.activity.gashapon.GashaponConfig;
import com.yanqu.road.server.manger.activity.gashapon.GashaponMgr;
import com.yanqu.road.server.manger.config.SystemOpenMgr;
import com.yanqu.road.server.manger.log.AutoLogMgr;
import com.yanqu.road.server.protocol.CrossProtocol;
import com.yanqu.road.server.protocol.GameErrorCode;
import com.yanqu.road.server.protocol.Protocol;
import com.yanqu.road.server.webservice.GamePublicFunction;
import com.yanqu.road.utils.RandomHelper;
import com.yanqu.road.utils.property.Property;
import com.yanqu.road.utils.property.PropertyHelper;
import com.yanqu.road.utils.string.StringUtils;

import java.util.*;

/**
 * @Author : Cxp
 * @Time : 2024-10-21
 * @Description : 幸运扭蛋-Module
 */

public class GashaponModule extends GeneralModule {

    private GashaponUserData userData;

    public GashaponModule(GamePlayer player) {
        super(player);
    }

    public GashaponUserData getUserData() {
        return userData;
    }

    @Override
    public boolean loadData() {
        ActivityInfo activityInfo = GashaponMgr.getActivityInfo();
        if (ActivityMgr.activityInShowTime(activityInfo)) {
            userData = new GashaponBusiness().getGashaponUserData(activityInfo.getActivityId(), player.getUserId());
        }
        return true;
    }

    @Override
    public boolean afterLoadData() {
        return true;
    }

    @Override
    public boolean saveData() {
        if (userData != null) {
            if (userData.isInsertOption()) {
                new GashaponBusiness().addUserData(userData);
            } else if (userData.isUpdateOption()) {
                new GashaponBusiness().updateUserData(userData);
            }
        }
        return true;
    }

    @Override
    public void afterLogin() {
        activityReload();
    }

    /**
     * 重新加载活动，包括同步配置、初始化用户、同步用户
     */
    public void activityReload() {
        if (!SystemOpenMgr.systemOpen(player, eSystemId.Gashapon.getValue())) {
            return;
        }

        GashaponConfig config = GashaponMgr.getConfig();
        if (config == null || !ActivityMgr.activityInShowTime(config.getActivityInfo())) {
            return;
        }

        syncConfig(config);
        initUserData(config.getActivityId());
        syncUserData();
    }

    /**
     * 同步配置，并发送给客户端
     *
     * @param config t_s_activity_lottery_reward
     */
    private void syncConfig(GashaponConfig config) {
        GashaponProto.GashaponConfigDataSyncMsg.Builder builder = GashaponProto.GashaponConfigDataSyncMsg.newBuilder();
        GashaponProto.GashaponConfigDataTemp.Builder inner = GashaponProto.GashaponConfigDataTemp.newBuilder();

        for (GashaponReward info : config.getRewardMap().values()) {
            GashaponProto.GashaponRewardTemp.Builder temp = GashaponProto.GashaponRewardTemp.newBuilder();
            temp.setId(info.getId());
            temp.setName(ServerLanguageMgr.getContent(info.getName(), player.getLanguage()));
            temp.setSelectNum(info.getSelectNum());
            temp.setBallNum(info.getBallNum());
            temp.setWeight(info.getWeight());
            List<String> prize = Arrays.asList(info.getPrizeList().split(";"));
            temp.addAllPrizeList(prize);
            temp.setIsNotice(info.getIsNotice());
            inner.addRewardList(temp);
        }
        builder.setConfigData(inner);

        player.sendPacket(Protocol.U_GASHAPON_CONFIG_DATA_SYNC, builder);
    }

    /**
     * 初始化用户
     *
     * @param activityId 活动ID
     */
    private synchronized void initUserData(int activityId) {
        if (userData == null || userData.getActivityId() != activityId) {
            GashaponUserData tempData = new GashaponUserData();
            tempData.setUserId(player.getUserId());
            tempData.setActivityId(activityId);
            tempData.setInsertOption();
            userData = tempData;
        }
    }

    /**
     * 同步用户
     */
    private void syncUserData() {
        if (userData == null) {
            return;
        }
        GashaponProto.GashaponUserDataSyncMsg.Builder builder = GashaponProto.GashaponUserDataSyncMsg.newBuilder();
        builder.setUserData(buildRet(userData));

        player.sendPacket(Protocol.U_GASHAPON_USER_DATA_SYNC, builder);
    }

    /**
     * 构建用户信息返回
     *
     * @param userData 用户信息
     * @return GashaponUserDataTemp
     */
    public GashaponProto.GashaponUserDataTemp.Builder buildRet(GashaponUserData userData) {
        GashaponProto.GashaponUserDataTemp.Builder builder = GashaponProto.GashaponUserDataTemp.newBuilder();

        builder.setScore(userData.getScore());
        builder.setNumToGood(GashaponMgr.getConfig().getGuaranteeNum() - userData.getGuaranteeCnt());
        builder.setDrawCnt(userData.getDrawCnt());
        builder.setTotalCost((long) userData.getDrawCnt() * Integer.parseInt(GashaponMgr.getConfig().getOneDrawCost().split("=")[1]));
        builder.setIsAnima(userData.isAnima());

        for (Map.Entry<Integer, Map<Integer, Integer>> kv : userData.getOptional().entrySet()) {
            for (Map.Entry<Integer, Integer> kv1 : kv.getValue().entrySet()) {
                GashaponProto.GashaponRewardChoose.Builder builder1 = GashaponProto.GashaponRewardChoose.newBuilder();
                builder1.setId(kv.getKey());
                builder1.setRewardId(kv1.getKey());
                builder1.setCnt(kv1.getValue());
                builder.addOptionalList(builder1);
            }
        }

        for (Map.Entry<Integer, Map<Integer, Integer>> kv : userData.getSelected().entrySet()) {
            for (Map.Entry<Integer, Integer> kv1 : kv.getValue().entrySet()) {
                GashaponProto.GashaponRewardChoose.Builder builder1 = GashaponProto.GashaponRewardChoose.newBuilder();
                builder1.setId(kv.getKey());
                builder1.setRewardId(kv1.getKey());
                builder1.setCnt(kv1.getValue());
                builder.addSelectedList(builder1);
            }
        }

        return builder;
    }

    /**
     * 选择奖励
     *
     * @param activityId 活动ID
     * @param chooseMap  奖励选择
     * @return ret
     */
    public int chooseReward(int activityId, Map<Integer, Map<Integer, Integer>> chooseMap) {
        int check = check(activityId);
        if (check != 0) {
            return check;
        }

        ActivityInfo activityInfo = GashaponMgr.getActivityInfo();
        if (ActivityMgr.activityInRewardPeriod(activityInfo)) { // 领奖期
            return GameErrorCode.E_GASHAPON_CAN_NOT_OPERATE_IN_SHOW_TIME;
        }

        if (userData.isLock()) {
            return GameErrorCode.E_GASHAPON_CAN_NOT_CHOOSE_REWARD;
        }

        Map<Integer, Map<Integer, Integer>> rewardMap = filteredRewardMap();
        GashaponConfig config = GashaponMgr.getConfig();
        Map<Integer, GashaponReward> configRewardMap = config.getRewardMap();
        Map<Integer, Map<Integer, Integer>> optionalMap = userData.getOptional(); // 全局已选的奖励
        Map<Integer, Map<Integer, Integer>> rewardRest = getRewardRest(rewardMap, optionalMap); // 全局未选的奖励

        Integer checkChoose = checkChoose(activityId, chooseMap, configRewardMap, rewardRest, rewardMap);
        if (checkChoose != null) {
            return checkChoose;
        }

        Map<Integer, Map<Integer, Integer>> selectedMap = userData.getSelected();
        selectedMap.clear();
        for (Map.Entry<Integer, Map<Integer, Integer>> kv : chooseMap.entrySet()) { // 初始化单局已抽奖励
            int qualityId = kv.getKey();
            if (!selectedMap.containsKey(qualityId)) {
                selectedMap.put(qualityId, new HashMap<>());
            }
            for (Map.Entry<Integer, Integer> kv1 : kv.getValue().entrySet()) {
                selectedMap.get(qualityId).put(kv1.getKey(), 0);
            }
        }

        userData.setChoose(chooseMap);
        userData.setSelected(selectedMap);
        GashaponProto.GashaponChooseRewardResp.Builder builder = GashaponProto.GashaponChooseRewardResp.newBuilder();
        builder.setRet(0);
        player.sendPacket(Protocol.U_GASHAPON_CHOOSE_REWARD, builder);

        return 0;
    }

    /**
     * 获取经过系统是否开启判断后的可选奖励
     *
     * @return 可选奖励数量-附带物品ID和数量 110111=1 -> 10
     */
    private Map<Integer, Map<Integer, String>> filteredRewardWithNumMap() {
        GashaponConfig config = GashaponMgr.getConfig();
        Map<Integer, Map<Integer, String>> res = new HashMap<>();

        for (Map.Entry<Integer, GashaponReward> kv : config.getRewardMap().entrySet()) { // 读取配置时过滤掉系统未开启的道具
            int qualityId = kv.getKey();
            if (!res.containsKey(qualityId)) {
                res.put(qualityId, new HashMap<>());
            }
            GashaponReward prize = kv.getValue();
            String prizeList = prize.getPrizeList();
            if (StringUtils.isNullOrEmpty(prizeList)) {
                continue;
            }
            String[] rewards = prize.getPrizeList().split(";"); // 111011=1,3,128
            for (String reward : rewards) {
                String[] items = reward.split(",");
                String[] goods = items[0].split("="); // 111011=1
                if (items.length == 3) {
                    if (!GamePublicFunction.isSystemOpen(player.getUserId(), Integer.parseInt(items[2]))) { // 判断系统是否开启
                        continue;
                    }
                }
                res.get(qualityId).put(Integer.parseInt(goods[0]), items[0]);
            }
        }

        return res;
    }

    /**
     * 获取经过系统是否开启判断后的可选奖励
     *
     * @return 可选奖励数量-仅附带物品Id  110111 -> 10
     */
    private Map<Integer, Map<Integer, Integer>> filteredRewardMap() {
        GashaponConfig config = GashaponMgr.getConfig();
        Map<Integer, Map<Integer, Integer>> res = new HashMap<>();

        for (Map.Entry<Integer, GashaponReward> kv : config.getRewardMap().entrySet()) { // 读取配置时过滤掉系统未开启的道具
            int qualityId = kv.getKey();
            if (!res.containsKey(qualityId)) {
                res.put(qualityId, new HashMap<>());
            }
            GashaponReward prize = kv.getValue();
            String prizeList = prize.getPrizeList();
            if (StringUtils.isNullOrEmpty(prizeList)) {
                continue;
            }
            String[] rewards = prize.getPrizeList().split(";"); // 111011=1,3,128
            for (String reward : rewards) {
                String[] items = reward.split(",");
                String[] goods = items[0].split("="); // 111011=1
                if (items.length == 3) {
                    if (!GamePublicFunction.isSystemOpen(player.getUserId(), Integer.parseInt(items[2]))) { // 判断系统是否开启
                        continue;
                    }
                }
                res.get(qualityId).put(Integer.parseInt(goods[0]), Integer.parseInt(items[1]));
            }
        }

        return res;
    }

    /**
     * 抽扭蛋
     *
     * @param activityId 活动ID
     * @param type       抽取类型 0-单抽 1-十连
     * @return ret
     */
    public synchronized int draw(int activityId, int type) {
        int check = check(activityId);
        if (check != 0) {
            return check;
        }

        ActivityInfo activityInfo = GashaponMgr.getActivityInfo();
        if (ActivityMgr.activityInRewardPeriod(activityInfo)) { // 领奖期
            return GameErrorCode.E_GASHAPON_CAN_NOT_OPERATE_IN_SHOW_TIME;
        }

        GashaponUserData cloned = userData.clone();
        GashaponConfig config = GashaponMgr.getConfig();
        Map<Integer, Map<Integer, Integer>> chooseMap = cloned.getChoose(); // 单局已选奖励
        Map<Integer, Map<Integer, Integer>> selectedMap = cloned.getSelected(); // 单局已抽奖励

        Map<Integer, Map<Integer, Integer>> singleRewardMap = new HashMap<>();
        for (Map.Entry<Integer, Map<Integer, Integer>> kv : chooseMap.entrySet()) { // 初始化单局未抽奖励
            int qualityId = kv.getKey();
            if (!singleRewardMap.containsKey(qualityId)) {
                singleRewardMap.put(qualityId, new HashMap<>());
            }
            for (Map.Entry<Integer, Integer> kv1 : kv.getValue().entrySet()) {
                singleRewardMap.get(qualityId).put(kv1.getKey(), config.getRewardMap().get(qualityId).getBallNum());
            }
        }
        Map<Integer, Map<Integer, Integer>> selectedRest = getRewardRest(singleRewardMap, selectedMap); // 实际单局未抽奖励
        int cnt = getCnt(type, config.getOneDrawCost(), selectedRest);
        if (cnt == 0) {
            return 0;
        }

        Map<Integer, Map<Integer, String>> rewardMapWithNum = filteredRewardWithNumMap();
        String rewardStr = getRewards(cnt, cloned, selectedRest, selectedMap, rewardMapWithNum);

        String[] rewards = rewardStr.split(";");
        cnt = rewards.length; // 根据奖励获取数量获得实际抽取的次数，针对提前抽到极品
        // 扣道具-元宝
        CurrencyModule currencyModule = player.getModule(CurrencyModule.class);
        Property cost = PropertyHelper.parseStringToProperty(config.getOneDrawCost());
        Property totalCost = cost.goodCountMultiNum(cnt);
        if (!currencyModule.removeCurrency(totalCost, eLogMoneyType.Gashapon, eLogMoneyType.GashaponCost)) {
            return GameErrorCode.E_BAG_ITEM_NO_ENOUGH;
        }

        userData.copy(cloned);
        userData.setDrawCnt(userData.getDrawCnt() + cnt);
        userData.setScore(userData.getScore() + (long) cnt * config.getOneDrawScore());
        currencyModule.addCurrency(rewardStr, eLogMoneyType.Gashapon, eLogMoneyType.GashaponReward);

        player.notifyListener(eGamePlayerEventType.GashaponUserRank.getValue(), userData.getScore()); // 更新排行榜
        int singleCost = Integer.parseInt(config.getOneDrawCost().split("=")[1]); // 一抽消耗
        // 更新元宝返利
        player.notifyListener(eGamePlayerEventType.GashaponIngotRebate.getValue(),
                (long) userData.getDrawCnt() * singleCost);

        GashaponProto.GashaponDrawResp.Builder builder = GashaponProto.GashaponDrawResp.newBuilder();
        builder.setRet(0);
        builder.setReward(rewardStr);
        builder.setUserData(buildRet(userData));
        player.sendPacket(Protocol.U_GASHAPON_DRAW, builder);

        Map<Integer, Map<String, Integer>> rewardMap = new HashMap<>();
        for (String str : rewards) {
            String[] items = str.split("=");
            int rewardId = Integer.parseInt(items[0]);
            for (Map.Entry<Integer, Map<Integer, String>> kv : rewardMapWithNum.entrySet()) {
                int qualityId = kv.getKey();
                Map<Integer, String> value = kv.getValue();
                if (value.containsKey(rewardId) && value.get(rewardId).equals(str)) {
                    if (!rewardMap.containsKey(qualityId)) {
                        rewardMap.put(qualityId, new HashMap<>());
                    }
                    rewardMap.get(qualityId).merge(value.get(rewardId), 1, Integer::sum);
                }
            }
        }
        sendLog(cnt, userData, config, rewardMap);

        return 0;
    }

    /**
     * 最终获取奖励
     *
     * @param cnt              抽取次数
     * @param user             user
     * @param selectedRest     单局未抽奖励
     * @param selectedMap      单局已抽奖励
     * @param rewardMapWithNum 可选奖励数量-附带物品ID和数量 110111=1 -> 10
     * @return Property
     */
    private String getRewards(int cnt, GashaponUserData user, Map<Integer, Map<Integer, Integer>> selectedRest,
                              Map<Integer, Map<Integer, Integer>> selectedMap,
                              Map<Integer, Map<Integer, String>> rewardMapWithNum) {
        GashaponConfig config = GashaponMgr.getConfig();
        StringBuilder rewards = new StringBuilder();
        List<GashaponProto.GashaponNoticeDataTemp> uploadList = new ArrayList<>(); // 公告

        for (int i = 0; i < cnt; i++) {
            String str = getRewardStr(user, selectedRest, config);
            if (str == null) {
                continue;
            }
            String[] ss = str.split(";");
            int qualityId = Integer.parseInt(ss[0]);
            int rewardId = Integer.parseInt(ss[1]);
            modifyGuaranteeCnt(user, selectedRest, qualityId);
            Map<Integer, Integer> originalReward = selectedRest.get(qualityId);
            if (originalReward.containsKey(rewardId) && originalReward.get(rewardId) > 0) { // 扣除抽到的奖励
                originalReward.merge(rewardId, -1, Integer::sum);
                if (originalReward.get(rewardId) == 0) {
                    originalReward.remove(rewardId);
                }
                // 累计到最后获得奖励中
                rewards.append(rewardMapWithNum.get(qualityId).get(rewardId)).append(";");
            }

            for (Integer qualityId1 : selectedRest.keySet()) { // 不存在某个品质挡位时，去除
                if (selectedRest.get(qualityId1).isEmpty()) {
                    selectedRest.remove(qualityId1);
                    break;
                }
            }
            if (!selectedMap.containsKey(qualityId)) { // 累计到单局已抽奖励
                selectedMap.put(qualityId, new HashMap<>());
            }
            selectedMap.get(qualityId).merge(rewardId, 1, Integer::sum);

            String rewardWithNum = rewardMapWithNum.get(qualityId).get(rewardId);
            if (config.getRewardMap().get(qualityId).getIsNotice() == 1) { // 判断是否需要公告
                uploadList.add(buildNotice(rewardWithNum, qualityId));
            }

            if (qualityId == GashaponConst.EXCELLENT) { // 抽到极品后不再继续
                break;
            }
        }

        if (!uploadList.isEmpty()) { // 上传公告
            GashaponProto.CrossGashaponUploadNoticeReq.Builder upload =
                    GashaponProto.CrossGashaponUploadNoticeReq.newBuilder();
            upload.setActivityId(user.getActivityId());
            upload.addAllNoticeList(uploadList);
            player.sendPacket(CrossProtocol.C_GASHAPON_UPLOAD_NOTICE, upload);
        }

        return rewards.substring(0, rewards.length() - 1);
    }

    /**
     * 发送日志
     *
     * @param cnt       抽取次数
     * @param user      玩家
     * @param config    config
     * @param rewardMap 奖励
     */
    private void sendLog(int cnt, GashaponUserData user, GashaponConfig config,
                         Map<Integer, Map<String, Integer>> rewardMap) {
        LogGashaponDraw log = new LogGashaponDraw();
        log.setActivityId(config.getActivityId());
        log.setUserId(player.getUserId());
        log.setDrawCnt(cnt);
        log.setNumToGuarantee(GashaponMgr.getConfig().getGuaranteeNum() - user.getGuaranteeCnt());
        log.setExtraOrdinary(rewardMap.containsKey(GashaponConst.EXTRAORDINARY));
        log.setExcellent(rewardMap.containsKey(GashaponConst.EXCELLENT));
        log.setScore((long) cnt * config.getOneDrawScore());
        log.setTotalScore(user.getScore());

        List<Map<String, Object>> logObjectMap = new ArrayList<>();
        for (Map.Entry<Integer, Map<String, Integer>> kv : rewardMap.entrySet()) {
            for (Map.Entry<String, Integer> kv1 : kv.getValue().entrySet()) {
                Map<String, Object> map = new HashMap<>();
                map.put("id", kv.getKey());
                String[] items = kv1.getKey().split("=");
                map.put("good", Integer.parseInt(items[0]));
                map.put("goodnum", Integer.parseInt(items[1]));
                map.put("num", kv1.getValue());
                logObjectMap.add(map);
            }
        }
        log.setReward(logObjectMap);
        log.setLogTime(new Date());
        AutoLogMgr.add(log);
    }

    /**
     * 修改非凡和极品的保底计数，累加或者重置
     *
     * @param user         玩家
     * @param selectedRest 单局未选奖励
     * @param qualityId    品质ID
     */
    private void modifyGuaranteeCnt(GashaponUserData user, Map<Integer, Map<Integer, Integer>> selectedRest,
                                    int qualityId) {
        if (qualityId == GashaponConst.EXCELLENT) {
            user.setLock(false); // 当局已解锁，可以重置
        }
        if (qualityId == GashaponConst.EXTRAORDINARY) {
            user.setGuaranteeCnt(0); // 重置保底
        } else if (qualityId == GashaponConst.EXCELLENT) {
            user.setExcellentCnt(0);
        }
        // 有非凡时，累计保底
        if (qualityId != GashaponConst.EXTRAORDINARY && selectedRest.containsKey(GashaponConst.EXTRAORDINARY)) {
            user.setGuaranteeCnt(user.getGuaranteeCnt() + 1);
        }
        // 有极品时，累计隐藏保底
        if (qualityId != GashaponConst.EXCELLENT && selectedRest.containsKey(GashaponConst.EXCELLENT)) {
            user.setExcellentCnt(user.getExcellentCnt() + 1);
        }
    }

    /**
     * 获取随机出的奖励字符串 品质;道具ID
     *
     * @param user         玩家
     * @param selectedRest 单局未选奖励
     * @param config       config
     * @return 品质;道具ID
     */
    private String getRewardStr(GashaponUserData user, Map<Integer, Map<Integer, Integer>> selectedRest,
                                GashaponConfig config) {
        Map<String, Integer> weightMap = new HashMap<>();
        Set<Integer> set; // 待随机的品质
        if (user.getGuaranteeCnt() == config.getGuaranteeNum() && selectedRest.containsKey(GashaponConst.EXTRAORDINARY)) {
            set = new HashSet<Integer>() {{
                add(GashaponConst.EXTRAORDINARY);
            }};
        } else if (user.getExcellentCnt() >= user.getExcellentTarget() && selectedRest.containsKey(GashaponConst.EXCELLENT)) { // 大于等于避免二者同时保底，隐藏保底累加已经大于目标值
            set = new HashSet<Integer>() {{
                add(GashaponConst.EXCELLENT);
            }};
        } else {
            set = new HashSet<>(Arrays.asList(GashaponConst.COMMON, GashaponConst.RARE, GashaponConst.OUTSTANDING,
                    GashaponConst.EXTRAORDINARY, GashaponConst.EXCELLENT));
        }

        for (Map.Entry<Integer, Map<Integer, Integer>> kv : selectedRest.entrySet()) {
            int qualityId = kv.getKey();
            if (set.contains(qualityId)) {
                for (Map.Entry<Integer, Integer> kv1 : kv.getValue().entrySet()) {
                    weightMap.put(qualityId + ";" + kv1.getKey(),
                            config.getRewardMap().get(qualityId).getWeight() * kv1.getValue());
                }
            }
        }
        RandomHelper rand = new RandomHelper();

        return rand.getRandomStrKeyByWeightEx(weightMap);
    }

    /**
     * 获取实际要抽取扭蛋的次数
     *
     * @param type           抽取类型 0-单抽 1-十连
     * @param oneDrawCostStr 单抽消耗
     * @param selectedRest   单局未抽奖励
     * @return 抽取扭蛋的次数
     */
    private int getCnt(int type, String oneDrawCostStr, Map<Integer, Map<Integer, Integer>> selectedRest) {
        int cnt = 0;

        if (type == GashaponConst.SINGLE) {
            cnt = GashaponConst.ONE;
        } else if (type == GashaponConst.DRAW_TEN) {
            cnt = GashaponConst.TEN;
        }
        CurrencyModule currencyModule = player.getModule(CurrencyModule.class);
        Property cost = PropertyHelper.parseStringToProperty(oneDrawCostStr);
        for (; cnt >= 0; cnt--) {
            Property totalCost = new Property(cost).goodCountMultiNum(cnt);
            if (currencyModule.currencyIsEnough(totalCost)) {
                break;
            }
        }

        int restCnt = 0; // 剩余扭蛋数量统计
        for (Map.Entry<Integer, Map<Integer, Integer>> kv : selectedRest.entrySet()) { // 获取剩余扭蛋数量
            for (Map.Entry<Integer, Integer> kv1 : kv.getValue().entrySet()) {
                restCnt += kv1.getValue();
            }
        }

        return Math.min(cnt, restCnt); // 抽取次数和剩余扭蛋数量进行比较， 避免扭蛋不够一次性抽取
    }

    /**
     * 锁定奖励，播放动画
     *
     * @param activityId 活动ID
     * @return ret
     */
    public int playAnimation(int activityId) {
        int check = check(activityId);
        if (check != 0) {
            return check;
        }

        ActivityInfo activityInfo = GashaponMgr.getActivityInfo();
        if (ActivityMgr.activityInRewardPeriod(activityInfo)) { // 领奖期
            return GameErrorCode.E_GASHAPON_CAN_NOT_OPERATE_IN_SHOW_TIME;
        }

        Map<Integer, Map<Integer, Integer>> rewardMap = filteredRewardMap();
        Map<Integer, Map<Integer, Integer>> chooseMap = userData.getChoose();
        Map<Integer, Map<Integer, Integer>> optionalMap = userData.getOptional();
        Map<Integer, GashaponReward> configRewardMap = GashaponMgr.getConfig().getRewardMap();
        Map<Integer, Map<Integer, Integer>> rewardRest = getRewardRest(rewardMap, optionalMap); // 全局未选的奖励

        Integer checkChoose = checkChoose(activityId, chooseMap, configRewardMap, rewardRest, rewardMap);
        if (checkChoose != null) {
            return checkChoose;
        }

        // 把单局已选奖励加到全局已选奖励中
        for (Map.Entry<Integer, Map<Integer, Integer>> kv : chooseMap.entrySet()) {
            int qualityId = kv.getKey(); // 品质id
            for (Map.Entry<Integer, Integer> kv1 : chooseMap.get(qualityId).entrySet()) {
                int rewardId = kv1.getKey();
                if (!optionalMap.containsKey(qualityId)) {
                    optionalMap.put(qualityId, new HashMap<>());
                }
                optionalMap.get(qualityId).merge(rewardId, 1, Integer::sum);
            }
        }
        userData.setOptional(optionalMap);

        userData.setLock(true);
        userData.setAnima(true);

        // 设置当前局极品奖励的隐藏保底数
        String[] range = GashaponMgr.getConfig().getHiddenGuaranteeNum().split("\\|");
        RandomHelper rand = new RandomHelper();
        userData.setExcellentTarget(rand.next(Integer.parseInt(range[0]), Integer.parseInt(range[1]) + 1));

        GashaponProto.GashaponUserDataSyncMsg.Builder builder = GashaponProto.GashaponUserDataSyncMsg.newBuilder();
        builder.setUserData(buildRet(userData));
        player.sendPacket(Protocol.U_GASHAPON_USER_DATA_SYNC, builder);

        GashaponProto.GashaponPlayAnimationMsgResp.Builder builder1 =
                GashaponProto.GashaponPlayAnimationMsgResp.newBuilder();
        builder1.setRet(0);
        player.sendPacket(Protocol.U_GASHAPON_PLAY_ANIMATION, builder1);

        return 0;
    }

    /**
     * 校验所选奖励是否合法
     *
     * @param activityId      活动ID
     * @param chooseMap       单局选择的奖励
     * @param configRewardMap 奖励的配置
     * @param rewardRest      全局未选的奖励
     * @param rewardMap       可选奖励数量-仅附带物品Id  110111 -> 10
     * @return Integer
     */
    private Integer checkChoose(int activityId, Map<Integer, Map<Integer, Integer>> chooseMap, Map<Integer,
            GashaponReward> configRewardMap, Map<Integer, Map<Integer, Integer>> rewardRest, Map<Integer, Map<Integer
            , Integer>> rewardMap) {
        if (chooseMap.size() != GashaponConst.QUALITY_COUNT) {
            return (int) GameErrorCode.E_GASHAPON_REWARD_ERROR;
        }

        for (Map.Entry<Integer, Map<Integer, Integer>> kv : chooseMap.entrySet()) {
            int qualityId = kv.getKey(); // 品质id
            int selectNum = Math.min(configRewardMap.get(qualityId).getSelectNum(), rewardRest.get(qualityId).size());
            for (Map.Entry<Integer, Integer> kv1 : chooseMap.get(qualityId).entrySet()) {
                int rewardId = kv1.getKey();
                Map<Integer, Integer> qualityRewardMap = rewardMap.get(qualityId);
                if (!qualityRewardMap.containsKey(rewardId)) { // 确认每个选择都是预设的奖励
                    log.error("活动ID{} 奖励{}不是预设的奖励或奖励相关系统未解锁", activityId, rewardId);
                    return (int) GameErrorCode.E_GASHAPON_REWARD_ERROR;
                }

                if (qualityRewardMap.containsKey(rewardId) && qualityRewardMap.get(rewardId) != -1) { // 确认可选次数足够
                    if (!rewardRest.get(qualityId).containsKey(rewardId)) {
                        return (int) GameErrorCode.E_GASHAPON_REWARD_NO_SELECTED_COUNT;
                    }
                }

                if (kv.getValue().size() != selectNum) { // 确认每个挡位必选奖励数量是否满足要求
                    return (int) GameErrorCode.E_GASHAPON_SELECT_NUM_ERROR;
                }
            }
        }

        return null;
    }

    /**
     * 重置奖励
     *
     * @param activityId 活动ID
     * @return ret
     */
    public int reset(int activityId) {
        int check = check(activityId);
        if (check != 0) {
            return check;
        }

        ActivityInfo activityInfo = GashaponMgr.getActivityInfo();
        if (ActivityMgr.activityInRewardPeriod(activityInfo)) { // 领奖期
            return GameErrorCode.E_GASHAPON_CAN_NOT_OPERATE_IN_SHOW_TIME;
        }

        if (userData.isLock()) {
            return GameErrorCode.E_GASHAPON_EXCELLENT_LOCKED;
        }

        userData.setAnima(false); // 重置动画
        userData.setGuaranteeCnt(0); // 重置保底，不保留到下一局
        userData.setExcellentCnt(0);
        userData.setSelected(new HashMap<>()); // 重置单局已抽奖励

        GashaponProto.GashaponResetMsgResp.Builder builder = GashaponProto.GashaponResetMsgResp.newBuilder();
        for (Map.Entry<Integer, Map<Integer, Integer>> kv : userData.getChoose().entrySet()) {
            for (Map.Entry<Integer, Integer> kv1 : kv.getValue().entrySet()) {
                GashaponProto.GashaponRewardChoose.Builder builder1 = GashaponProto.GashaponRewardChoose.newBuilder();
                builder1.setId(kv.getKey());
                builder1.setRewardId(kv1.getKey());
                builder1.setCnt(kv1.getValue());
                builder.addLastSelectedList(builder1);
            }
        }
        builder.setRet(0);
        player.sendPacket(Protocol.U_GASHAPON_RESET, builder);

        return 0;
    }

    /**
     * 获取奖励剩余
     *
     * @param original 原有奖励，全局奖励或者单局奖励
     * @param selected 累计奖励，全局已选奖励或者单局已选奖励
     * @return 奖励剩余
     */
    private Map<Integer, Map<Integer, Integer>> getRewardRest(Map<Integer, Map<Integer, Integer>> original,
                                                              Map<Integer, Map<Integer, Integer>> selected) {
        Map<Integer, Map<Integer, Integer>> res = new HashMap<>();

        for (Map.Entry<Integer, Map<Integer, Integer>> kv : original.entrySet()) {
            int qualityId = kv.getKey();
            if (!res.containsKey(qualityId)) {
                res.put(qualityId, new HashMap<>());
            }
            for (Map.Entry<Integer, Integer> kv1 : kv.getValue().entrySet()) {
                res.get(qualityId).put(kv1.getKey(), kv1.getValue());
            }
        }

        Set<Integer> removeIds = new HashSet<>();
        for (Map.Entry<Integer, Map<Integer, Integer>> kv : res.entrySet()) {
            int qualityId = kv.getKey();
            if (selected.containsKey(qualityId)) {
                for (Map.Entry<Integer, Integer> kv1 : selected.get(qualityId).entrySet()) {
                    int rewardId = kv1.getKey();
                    res.get(qualityId).merge(rewardId, -kv1.getValue(), Integer::sum);
                    if (res.get(qualityId).get(rewardId) == 0) {
                        res.get(qualityId).remove(rewardId);
                    }
                }
                if (res.get(qualityId).isEmpty()) {
                    removeIds.add(qualityId);
                }
            }
        }

        for (Integer qualityId : removeIds) {
            res.remove(qualityId);
        }

        return res;
    }

    /**
     * 构建公告信息返回
     *
     * @param reward 奖励
     * @param type   奖励类型
     * @return GashaponNoticeDataTemp
     */
    private GashaponProto.GashaponNoticeDataTemp buildNotice(String reward, int type) {
        GashaponProto.GashaponNoticeDataTemp.Builder builder = GashaponProto.GashaponNoticeDataTemp.newBuilder();
        builder.setActivityId(userData.getActivityId());
        builder.setUserId(userData.getUserId());
        builder.setServerId(GameServer.getInstance().getServerId());
        builder.setNickName(player.getUserInfo().getNickName());
        builder.setType(type);
        builder.setReward(reward);
        builder.setTime(System.currentTimeMillis());

        return builder.build();
    }

    /**
     * 跨服获取公告
     *
     * @param reqMsg 请求
     * @return ret
     */
    public int getNotice(GashaponProto.GashaponNoticeMsgReq reqMsg) {
        if (reqMsg == null) {
            return GameErrorCode.E_GASHAPON_GET_NOTICE_FAIL;
        }
        int check = check(reqMsg.getActivityId());
        if (check != 0) {
            return check;
        }
        player.sendPacket(CrossProtocol.C_GASHAPON_GET_NOTICE, reqMsg.toBuilder());

        return 0;
    }

    /**
     * 简单检查
     *
     * @param activityId 活动ID
     * @return ret
     */
    public int check(int activityId) {
        ActivityInfo activityInfo = GashaponMgr.getActivityInfo();
        if (activityInfo == null) {
            return GameErrorCode.E_GASHAPON_ACTIVITY_NOT_OPEN;
        }
        if (activityInfo.getActivityId() != activityId) {
            return GameErrorCode.E_GASHAPON_ACTIVITY_ID_ERROR;
        }

        if (!ActivityMgr.activityInShowTime(activityInfo)) {
            return GameErrorCode.E_GASHAPON_ACTIVITY_NO_IN_SHOW_TIME;
        }
        if (!SystemOpenMgr.systemOpen(player, eSystemId.Gashapon.getValue())) {
            return GameErrorCode.E_GASHAPON_ACTIVITY_LOCK;
        }
        GashaponConfig config = GashaponMgr.getConfig();
        if (config == null) {
            return GameErrorCode.E_GASHAPON_ACTIVITY_CONFIG_ERROR;
        }
        if (userData == null || userData.getActivityId() != activityId) {
            initUserData(activityId);
        }
        if (userData == null || userData.getActivityId() != activityId) {
            return GameErrorCode.E_GASHAPON_ACTIVITY_NOT_OPEN;
        }

        return 0;
    }
}
